#include <algorithm>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <vector>

using namespace std;

/// globals...
const int minBound = 1;
const int maxBound = 10;
const int n = 4;

/// class binary operations
class binaryFuncBase
{
public: 
	virtual string symbol() const = 0; 
	virtual bool isCommutative() = 0; 
	virtual bool isAssociative() = 0;
	virtual double apply(double lhs, double rhs) const = 0;
	virtual int priority() const  = 0;
	virtual bool isAppliable(double lhs, double rhs) {return true;} // for convenient
};




class resExprBase
{
public: 
friend bool operator<(const resExprBase& lhs, const resExprBase& rhs);
	virtual double getValue() const  = 0; 
	virtual const binaryFuncBase* getRootOper() const = 0; 
	virtual const int getOperandNum() = 0;
	virtual const resExprBase* getOperand(int index) const = 0;
	virtual const string& toStr() const  = 0;

	virtual bool isSmallerThan(const resExprBase* rhs) const = 0;
	virtual bool isGreaterThan(const resExprBase* rhs) const = 0;
};

bool operator<(const resExprBase& lhs, const resExprBase& rhs)
{
	// return lhs.toStr() < rhs.toStr();
	if (lhs.getValue() < rhs.getValue()) return true;
	if (rhs.getValue() < lhs.getValue()) return false;

	// equal value
	return lhs.isSmallerThan(rhs);
}

class resExprDoubleWrapper : public resExprBase
{
public : 
	virtual double getValue() const {return m_value;}; 
	virtual const binaryFuncBase* getRootOper() const {return nullptr;}; 
	virtual const int getOperandNum() {return 1;};
	virtual const resExprBase* getOperand(int index) const {if (index == 0) return this; return nullptr;};
	virtual const string& toStr() const {stringstream ss; ss << m_value; return ss.str();}

	resExprDoubleWrapper(double value) : 
	m_value(value)
	{}
private: 
	double m_value;
};

class resExprBinaryOperation : public resExprBase
{
public: 
friend ostream& operator<<(ostream & os, const resExprBinaryOperation& expr);
	virtual double getValue() const {/*assert*/return m_op->apply(getOperand(0)->getValue(), getOperand(1)->getValue());} 
	virtual const binaryFuncBase* getRootOper() const {return m_op;}
	virtual const int getOperandNum() {return 2;}
	virtual const resExprBase* getOperand(int index) const {if (index >=0 && index <2) return m_operands[index]; else return 0;}
	virtual const string& toStr() const {if (memo.length() == 0) {stringstream ss; ss << *this; memo = ss.str();}return memo; }

	resExprBinaryOperation(binaryFuncBase * pOp, const resExprBase* lhs, const resExprBase* rhs) :
	m_op(pOp)
	{
		m_operands[0] = (lhs);
		m_operands[1] = (rhs);
    }

private: 
	mutable string           memo; 
	binaryFuncBase * m_op; 
	const resExprBase    * m_operands[2]; 
};

ostream& operator<<(ostream & os, const resExprBinaryOperation& expr)
{
	// os << expr.getOperand(0)->toStr() << expr.getRootOper()->symbol() << expr.getOperand(1)->toStr();
	const resExprBase* lhs = expr.getOperand(0);
	const resExprBase* rhs = expr.getOperand(1);
	
	bool addP1 = false;
	if (lhs->getRootOper() != 0)
	{
		if (lhs->getRootOper()->priority() < expr.getRootOper()->priority()) 
		{
			addP1 = true;
		}
	}
	if (addP1)
	{
		os << "(";
	}
	os << expr.getOperand(0)->toStr(); 
	if (addP1)
	{
		os << ")";
	}

	os << expr.getRootOper()->symbol();
	// need do reverse

	bool addP2 = false;
	if (rhs->getRootOper() != 0)
	{
		if (rhs->getRootOper()->priority() < expr.getRootOper()->priority()) 
		{
			addP2 = true;
		}
		else if (rhs->getRootOper()->priority() == expr.getRootOper()->priority() && (expr.getRootOper()->symbol() == " - " || expr.getRootOper()->symbol() == "/"))
		{
			addP2 = true;
		}
	}
	if (addP2)
	{
		os << "(";
	}
	os << expr.getOperand(1)->toStr(); 
	if (addP2)
	{
		os << ")";
	}


	return os;
}



class adding : public binaryFuncBase
{
public: 
	virtual string symbol() const {return " + ";}
	virtual bool isCommutative() {return true;} 
	virtual bool isAssociative() {return true;} 
	virtual double apply(double lhs, double rhs) const {return lhs + rhs;}
	virtual int priority() const {return 1;}
};

class subtracting : public binaryFuncBase
{
public: 
	virtual string symbol() const {return " - ";}
	virtual bool isCommutative() {return false;} 
	virtual bool isAssociative() {return false;} 
	virtual double apply(double lhs, double rhs) const {return lhs - rhs;}
	virtual int priority() const {return 1;}
};

class multiplication : public binaryFuncBase
{
public: 
	virtual string symbol() const {return " * ";}
	virtual bool isCommutative() {return true;} 
	virtual bool isAssociative() {return true;} 
	virtual double apply(double lhs, double rhs) const {return lhs * rhs;}
	virtual int priority() const {return 2;}
};

class dividing : public binaryFuncBase
{
public: 
	virtual string symbol() const {return " / ";}
	virtual bool isCommutative() {return false;} 
	virtual bool isAssociative() {return false;} 
	virtual double apply(double lhs, double rhs) const {return lhs / rhs;}
	virtual int priority() const {return 2;}
};

class powering : public binaryFuncBase
{
public :
	virtual string symbol() const {return " ** ";}
	virtual bool isCommutative() {return false;} 
	virtual bool isAssociative() {return false;} 
	virtual double apply(double lhs, double rhs) const 
	{
		// TODO : implementation
		return 1;		
	}
	virtual int priority() const {return 3;}
	virtual bool isAppliable(double lhs, double rhs) {if (lhs == 0 && rhs == 0) return false; return true;}
};

vector<binaryFuncBase*> binaryFuncArray;

typedef map<double, vector<resExprBase*> > resultType; 
typedef resultType::value_type singleRes;

struct resExprBasePointerCompare : public binary_function<resExprBase*, resExprBase*, bool>
{
	bool operator() (const resExprBase* lhs, const resExprBase* rhs) const 
	{
		return lhs->toStr() < rhs->toStr();
	}
};

struct resExprBasePointerEqual : public binary_function<resExprBase*, resExprBase*, bool>
{
	bool operator() (const resExprBase* lhs, const resExprBase* rhs) const 
	{
		return lhs->toStr() == rhs->toStr();
	}
};

map<double, resultType> memo1; 
void solve(double num, resultType & res)
{
	if (memo1.find(num) != memo1.end()) 
	{
		res = memo1[num];
		return ;
	}
	res.clear();


	stringstream ss; 
	ss << num; 
 
	res[num].push_back(new resExprDoubleWrapper(num));

	return ;
}

void solve(const singleRes& singleRes1, const singleRes& singleRes2, resultType & res)
{
	res.clear();

	for (int i = 0; i < binaryFuncArray.size(); ++i)
	{
		binaryFuncBase * curFunc = binaryFuncArray[i];
		double value;
		string curExpr;
		double a = singleRes1.first; 
		double b = singleRes2.first; 

		if (curFunc->isAppliable(a, b))
		{
			value = curFunc->apply(a, b);
			for (int j = 0; j < singleRes1.second.size(); ++j)
				for (int k = 0; k < singleRes2.second.size(); ++k)
				{					
					res[value].push_back(new resExprBinaryOperation(curFunc, singleRes1.second[j], singleRes2.second[k]));
				}
			sort(res[value].begin(), res[value].end(), resExprBasePointerCompare());
			res[value].erase(unique(res[value].begin(), res[value].end(), resExprBasePointerEqual()), res[value].end());
		}
		
		if (a != b && !curFunc->isCommutative() && curFunc->isAppliable(b, a))
		{
			value = curFunc->apply(b, a);
			for (int j = 0; j < singleRes1.second.size(); ++j)
				for (int k = 0; k < singleRes2.second.size(); ++k)
				{					
					res[value].push_back(new resExprBinaryOperation(curFunc, singleRes2.second[k], singleRes1.second[j]));
				}
			sort(res[value].begin(), res[value].end(), resExprBasePointerCompare());
			res[value].erase(unique(res[value].begin(), res[value].end(), resExprBasePointerEqual()), res[value].end());
		}
	}
}

void mergeRes(resultType & res1, const resultType & res2)
{
	for (resultType::const_iterator cit1 = res2.begin(); cit1 != res2.end(); ++cit1)
	{
		bool bNotUnique = false;
		if (res1.find(cit1->first) != res1.end()) bNotUnique = true;

		res1[cit1->first].insert(res1[cit1->first].end(), cit1->second.begin(), cit1->second.end());
		if (bNotUnique)
		{
			sort(res1[cit1->first].begin(), res1[cit1->first].end(), resExprBasePointerCompare());
			res1[cit1->first].erase(unique(res1[cit1->first].begin(), res1[cit1->first].end(), resExprBasePointerEqual()), res1[cit1->first].end());
		}
	}
}

void descartesRes(const resultType & res1, const resultType & res2, resultType & res)
{
	for (resultType::const_iterator cit1 = res1.begin(); cit1 != res1.end(); ++cit1)
	{
		for (resultType::const_iterator cit2 = res2.begin(); cit2 != res2.end(); ++cit2)
		{
			resultType temp;
			if (cit2->first < cit1->first)
				solve(*cit2, *cit1, temp);
			else 
				solve(*cit1, *cit2, temp);
			mergeRes(res, temp);
		}
	}
}

void solve(const vector<double> & nums, resultType & res)
{
	res.clear();
	if (nums.size() == 0)
	{
		return ;
	}

	if (nums.size() == 1)
	{
		return solve(nums[0], res);
	}

	if (nums.size() == 2)
	{
		resultType res1, res2; 
		solve(nums[0], res1); 
		solve(nums[1], res2);
		descartesRes(res1, res2, res);
		return ;
	}
	
	for (int i = 0; i < nums.size(); ++i)
		for (int j = i; j < nums.size(); ++j) 
		{
			vector<double> lnums(nums);
			lnums.erase(lnums.begin() + max(i, j));
			if (i != j)
				lnums.erase(lnums.begin() + min(i, j));
				
			resultType curresOthers; 
			solve(lnums, curresOthers);
				
			lnums.clear(); 
			lnums.push_back(nums[i]);
			if (i != j)
				lnums.push_back(nums[j]);

			resultType cur;
			solve(lnums, cur);
			
			descartesRes(cur, curresOthers, res);
		}

	return ;
}

void solve(const vector<int>& nums, resultType &res)
{
	vector<double> converted;
	for (int i = 0; i < nums.size(); ++i)
	{
		converted.push_back(nums[i]);
	}

	solve(converted, res);
}

vector<int> nextNumbers(const vector<int>& nums)
{
	vector<int> res(nums);
	int i = res.size() - 1;
	for (; i >= 0; --i)
	{
		if (res[i] == maxBound) 
		{
			res[i] = minBound; 
		}
		else
		{
			res[i]++;
			break;
		}
	}
	if (i == -1)
	{
		res.insert(res.begin(), minBound);
	}
	
	return res;
}

int notSolvable = 0;

void printRes(const vector<int> & numbers, resultType& res)
{
	vector<resExprBase*> & toPri = res[24];
	if (toPri.size() == 0) 
	{
		notSolvable++;
		return ;
	}

	for (int i = 0; i < numbers.size(); ++i)
	{
		cout << numbers[i] << ' ';
	}
	cout << endl;


	for (int i = 0; i < toPri.size(); ++i)
	{
		cout << toPri[i]->toStr() << endl;
	}
	cout << "---------------------------------------------------------------" << endl;; 
}

void printHelp()
{
}


int main(int argc, char * argv[])
{
	adding a; binaryFuncArray.push_back(&a);
	subtracting s; binaryFuncArray.push_back(&s);
	multiplication m; binaryFuncArray.push_back(&m);
	dividing d; binaryFuncArray.push_back(&d);
	
	if (argc == 1)
	{
		vector<int> numbers(n, minBound);
		vector<int> endNum(n + 1, minBound);
		set<vector<int> > memos;
		resultType res; 
		while (numbers != endNum)
		{
			vector<int> curNum(numbers);
			sort(curNum.begin(), curNum.end());
			if (memos.find(curNum) == memos.end())
			{
				solve(curNum, res);

				printRes(curNum, res);

				memos.insert(curNum);
			}

			numbers = nextNumbers(numbers);
		}
	}
	else if (argc == 5) // 
	{
		system("pause");
		vector<int> pro2; 
		stringstream ss; 
		for (int i = 1; i < argc; ++i)
		{
			ss << argv[i];
			int cur; 
			ss >> cur; 
			ss.clear(); 
			ss.str("");
			pro2.push_back(cur);
		}
		resultType res;
		solve(pro2, res);
		printRes(pro2, res);
	} 
	else 
	{
		printHelp();
	}
	return 0;
}